#ifndef __TEXTURE_H
#define __TEXTURE_H

#include "config.h"
#include "matrix.h"

/*
class FRGBA 
{
public:
	FRGBA () : r(color[0]), g(color[1]), b(color[2]), a(color[3]) 
	{
		r = g = b = a = 0.0; 
	}
	FRGBA (float _r, float _g, float _b, float _a)
		: r(color[0]), g(color[1]), b(color[2]), a(color[3])
	{
		r = _r, g = _g, b = _b, a = _a; 
	}
public :
	//float r, g, b , a; 
	math::vector<float, 4> color ; 
	float & r, & g, & b,  & a; 
};
*/

class TexCoord
{
public:
	TexCoord () : x(0.0f), y (0.0f) {}
	TexCoord (float _x, float _y) : x(_x), y (_y) {}
public:
	float x, y ;
};

class TextureMemory2D
{
public :
	/**
	 * height --> row --> y
	 * width  --> col --> x
	 */
	typedef math::vector<float, 4> frgba_type ;
	TextureMemory2D (unsigned row, unsigned col) : m_row (row), m_col (col) 
	{
		this->m_videoMemory = new float [4 * m_row * m_col] ;
		if (this->m_videoMemory == NULL ) ASSERT ("Memory Allocation Fail.");
	}
	unsigned get_col() const {return this->m_col ; }
	unsigned get_row() const {return this->m_row ; }

	float get_texXOffset () const {return 1.0; }
	float get_texYOffset () const {return 1.0 ; }
	float get_texOffset () const { return 1.0;}

	float * get_memory () const 
	{
		return this->m_videoMemory ;
	}


	TexCoord get_texCoord (unsigned row, unsigned col ) 
	{
		TexCoord coord ;
		coord.x = col; 
		coord.y = row; 
		return coord ;
	}

	void read (unsigned row, unsigned col, frgba_type & color) 
	{
		unsigned offset = get_offset(row, col ); 
		color.r = m_videoMemory[offset + 0]; 
		color.g = m_videoMemory[offset + 1]; 
		color.b = m_videoMemory[offset + 2]; 
		color.a = m_videoMemory[offset + 3]; 
	}
	void write (unsigned row, unsigned col, const frgba_type & color) 
	{
		unsigned offset = get_offset (row, col);
		m_videoMemory[offset + 0] = color.r ;
		m_videoMemory[offset + 1] = color.g ;
		m_videoMemory[offset + 2] = color.b ;
		m_videoMemory[offset + 3] = color.a ;		
	}

	void write (unsigned row, unsigned col, unsigned offset, const float value)
	{
		offset += get_offset(row, col);
		m_videoMemory[offset] = value ; 
	}

	~TextureMemory2D () {delete this->m_videoMemory ;}
protected :
	/* Col has high priority. */
	unsigned get_offset (unsigned row, unsigned col) { return 4 * ( row * m_col + col) ; }
private :
	unsigned m_col, m_row ;
	float * m_videoMemory ;

};

class Texture 
{
public :
	typedef math::vector<float, 4> frgba_type ;

	Texture (unsigned height, unsigned width): m_row(height), m_col(width) 
	{		
		mp_texture = new TextureMemory2D (height, width ) ;
		mp_subtexture = new TextureMemory2D (1, 1);
	}
	~ Texture () 
	{
		delete mp_texture ; 
		delete mp_subtexture ;
	}
public :
	void init()
	{
		std::cout<<"INIT TEX BEGIN"<<std::endl;
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		std::cout<<"INIT TEX BEGIN 1"<<std::endl;
		glGenTextures(1, &m_texName);
		std::cout<<"INIT TEX BEGIN 2"<<std::endl;
		glBindTexture(GL_TEXTURE_RECTANGLE, m_texName);
		std::cout<<"INIT TEX BEGIN 3"<<std::endl;
		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_REPEAT);
		std::cout<<"INIT TEX BEGIN 4"<<std::endl;
		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_REPEAT);
		std::cout<<"INIT TEX BEGIN 5"<<std::endl;
		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, 
						GL_NEAREST);
		std::cout<<"INIT TEX BEGIN 6"<<std::endl;
		glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, 
						GL_NEAREST);
		std::cout<<"INIT TEX BEGIN 7"<<std::endl;
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		std::cout<<"INIT TEX BEGIN 8"<<std::endl;
/*
		glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA32F_ARB, mp_texture->get_col(), mp_texture->get_row(), 0, GL_RGBA, GL_FLOAT, mp_texture->get_memory() );
*/
		glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA32F_ARB, mp_texture->get_col(), mp_texture->get_row(), 0, GL_RGBA, GL_FLOAT, mp_texture->get_memory() );
		std::cout<<"INIT TEX END"<<std::endl;
	}


	void write (unsigned row, unsigned col, const frgba_type & color)
	{
		this->mp_texture->write (row, col, color);
	}
	void update (unsigned row, unsigned col, const frgba_type & color)
	{
		this->mp_subtexture->write (0, 0, color);
		//glBindTexture(GL_TEXTURE_RECTANGLE, m_texName);
		glTexSubImage2D (GL_TEXTURE_RECTANGLE, 0, 
						 col, row, 
						 1, 1, GL_RGBA, GL_FLOAT, mp_subtexture->get_memory());	
	}
private :
	GLuint m_texName;
	unsigned m_row, m_col;
	TextureMemory2D * mp_texture ;
	TextureMemory2D * mp_subtexture; 
};
#endif
